/*******************************************************************************
** 文件名称：led.c
** 文件作用：LED
** 编写作者：Tom Free 付瑞彪
** 编写时间：2021-05-25
** 文件备注：
** 更新记录：
**           2021-05-25 -> 创建文件
**                                                            <Tom Free 付瑞彪>
**
**           Copyright (c) 2018-2021 付瑞彪 All Rights Reserved
**
**       1 Tab == 4 Spaces     UTF-8     ANSI C Language(C99)
*******************************************************************************/

#include "led.h"
#include "gm_utils.h"
#include "string.h"

/* 端口pin脚掩码定义 */
#define HC595_OE        0x01
#define HC595_RCLK      0x02
#define HC595_SRCLK     0x04
#define HC595_D1        0x08
#define HC595_D2        0x10
#define HC595_D3        0x20
#define HC595_D4        0x40
#define BACK_LED        0x80

/* 字定义 */
typedef union
{
    uint16_t value;
    struct
    {
        uint8_t low;
        uint8_t high;
    };
} word_value_t;

/* led控制参数定义 */
typedef struct
{
    uint8_t row_cnt;            /* 列计数器 */
    bool    needle_out_en;      /* 指针LED是否输出使能 */
    bool    bg_out_en;          /* 背景LED是否输出使能 */
    uint8_t bg_brightness;      /* 背景LED亮度值，1-11 */
} led_args_t;

/* 11列扫描表1，用于快速查表扫描 */
static const word_value_t row_scan_list[] =
{
    0x0010,
    0x0200,
    0x0400,
    0x0008,
    0x0020,
    0x0040,
    0x0800,
    0x1000,
    0x0004,
    0x0002,
    0x2000,
};

/* line缓存 */
static word_value_t line_buf[] =
{
    0x0000,
    0x0000,
    0x0000,
    0x0000,
    0x0000,
    0x0000,
    0x0000,
    0x0000,
    0x0000,
    0x0000,
    0x0000,
};

/* led控制参数 */
static led_args_t led_args;

/* LED初始化 */
void led_init(void)
{
    /* 输出 */
    DDRA = 0xFF;
    /* 低电平 */
    PORTA = 0x7F;

    memset(line_buf, 0, sizeof(line_buf));
    led_args.row_cnt = 0;
    led_args.needle_out_en = true;
    led_scan_one_row();
    led_args.needle_out_en = false;
    led_args.bg_brightness = 1;
}

/* led输出使能 */
void led_enable(void)
{
    /* 备份状态寄存器 */
    uint8_t tmp = SREG;

    __disable_interrupt();

    led_args.needle_out_en = true;
    PORTA &= 0xFE;

    SREG = tmp;
}

/* led输出不使能 */
void led_diable(void)
{
    /* 备份状态寄存器 */
    uint8_t tmp = SREG;

    __disable_interrupt();

    led_args.needle_out_en = false;
    PORTA |= 0x01;

    SREG = tmp;
}

/* 背景LED输出使能 */
void led_bg_enable(void)
{
    /* 备份状态寄存器 */
    uint8_t tmp = SREG;

    __disable_interrupt();

    led_args.bg_out_en = true;
    PORTA |= 0x80;

    SREG = tmp;
}

/* 背景LED输出不使能 */
void led_bg_disable(void)
{
    /* 备份状态寄存器 */
    uint8_t tmp = SREG;

    __disable_interrupt();

    led_args.bg_out_en = false;
    PORTA &= 0x7F;

    SREG = tmp;
}

/* led输出状态 */
bool led_out_status(void)
{
    return led_args.needle_out_en;
}

/* led背景输出状态 */
bool led_bg_out_status(void)
{
    return led_args.bg_out_en;
}

/* 设置背景亮度 */
void led_set_bg_brightness(uint8_t value)
{
    if (value < 1)
    {
        value = 1;
    }
    else if (value > 11)
    {
        value = 11;
    }

    CHIP_NEW_CRITICAL();
    CHIP_ENTER_CRITICAL();
    led_args.bg_brightness = value;
    CHIP_EXIT_CRITICAL();
}

/* 写入时间 */
void led_write_time(gm_time_t *ptime)
{
    uint8_t line, offset;

    CHIP_NEW_CRITICAL();
    CHIP_ENTER_CRITICAL();

    memset(line_buf, 0, sizeof(line_buf));

    line = ptime->second / 12;
    offset = ptime->second % 12;
    line_buf[line].value = 1 << offset;

    line = ptime->minute / 12 + 5;
    offset = ptime->minute % 12;
    line_buf[line].value = 1 << offset;

    offset = ptime->hour;
    if (offset > 12)
    {
        offset = offset - 12;
    }
    if (offset == 0)
    {
        offset = 11;
    }
    else
    {
        offset--;
    }
    line_buf[10].value = 1 << offset;
    CHIP_EXIT_CRITICAL();
}

/* 清空时数据，不显示时 */
void led_clear_hour(void)
{
    CHIP_NEW_CRITICAL();
    CHIP_ENTER_CRITICAL();
    line_buf[10].value = 0;
    CHIP_EXIT_CRITICAL();
}

/* 清空分数据，不显示分 */
void led_clear_minute(void)
{
    CHIP_NEW_CRITICAL();
    CHIP_ENTER_CRITICAL();
    line_buf[5].value = 0;
    line_buf[6].value = 0;
    line_buf[7].value = 0;
    line_buf[8].value = 0;
    line_buf[9].value = 0;
    CHIP_EXIT_CRITICAL();
}

/* 清空秒数据，不显示秒 */
void led_clear_second(void)
{
    CHIP_NEW_CRITICAL();
    CHIP_ENTER_CRITICAL();
    line_buf[0].value = 0;
    line_buf[1].value = 0;
    line_buf[2].value = 0;
    line_buf[3].value = 0;
    line_buf[4].value = 0;
    CHIP_EXIT_CRITICAL();
}

/* LED扫描一列，每1ms调用一次 */
void led_scan_one_row(void)
{
    uint8_t i, tmp;
    uint8_t dat1, dat2, dat3, dat4;

    if (led_args.needle_out_en)
    {
        dat1 = line_buf[led_args.row_cnt].low;
        dat2 = line_buf[led_args.row_cnt].high;
        dat3 = row_scan_list[led_args.row_cnt].low;
        dat4 = row_scan_list[led_args.row_cnt].high;

        i = 8;
        tmp = PORTA;

        while (i)
        {
            tmp &= ~0x7E;
            PORTA = tmp;
            tmp |= (dat1 & 0x80) >> 4;
            tmp |= (dat2 & 0x80) >> 3;
            tmp |= (dat3 & 0x80) >> 2;
            tmp |= (dat4 & 0x80) >> 1;
            PORTA = tmp;
            tmp |= HC595_SRCLK;
            PORTA = tmp;
            dat1 <<= 1;
            dat2 <<= 1;
            dat3 <<= 1;
            dat4 <<= 1;
            i--;
        }
        tmp |= HC595_RCLK;
        PORTA = tmp;
        tmp &= ~0x7E;
    }
    if (led_args.bg_out_en)
    {
        if (led_args.row_cnt < led_args.bg_brightness)
        {
            tmp |= 0x80;
        }
        else
        {
            tmp &= 0x7F;
        }
    }
    PORTA = tmp;

    led_args.row_cnt++;
    led_args.row_cnt %= 11;
}
